#!/usr/bin/env python
# encoding=utf-8
#codeby     道长且阻
#email      ydhcui@suliu.net/QQ664284092
#website    http://www.suliu.net


import ftplib
import telnetlib
import socket
import binascii
import hashlib
import struct
import re
import time


@PluginsManage.register()
class RedisNoAuth(BaseHostPlugin):
    service  = 'redis'
    vul_name = "Redis 未授权访问"
    vul_rank = '高'
    vul_desc = """redis 默认不需要密码即可访问，黑客直接访问即可获取数据库中所有信息，造成严重的信息泄露。"""
    vul_plan = """解决方案
                配置bind选项, 限定可以连接Redis服务器的IP, 并修改redis的默认端口6379.
                配置AUTH, 设置密码, 密码会以明文方式保存在redis配置文件中.
                配置rename-command CONFIG "RENAME_CONFIG", 这样即使存在 未授权访问, 也能够给攻击者使用config指令加大难度
                好消息是Redis作者表示将会开发”real user”，区分普通用户和admin权限，普通用户将会被禁止运行某些命令，如config
                """
    def filter(self,port=6379):
        '''后台会调用该函数，匹配为真时才会进行该插件的测试'''
        if self.port == port:
            return True

    def check(self,user='',pwd='',timeout=10):
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        try:
            s.connect((self.host,int(self.port)))
            s.send("INFO\r\n")
            recvdata = s.recv(1024)
            if 'redis_version' in recvdata:
                self.payload = "username:%s,password:%s" % (user,pwd)
                return True
            elif "Authentication" in result:
                s.send("AUTH %s\r\n"%(pwd))
                result = s.recv(1024)
                if '+OK' in result:
                   self.payload = "username:%s,password:%s" % (user,pwd)
                   return True
        except Exception as e:
            log.info(e)
            return False
        finally:
            s.close()

@PluginsManage.register()
class MemcachedNoAuth(BaseHostPlugin):
    service  = 'memcached'
    vul_name = "Memcached 未授权访问"
    vul_rank = '高'
    vul_desc = """"""
    vul_plan = """"""
    def filter(self,port=11211):
        if self.port == port
            return True

    def check(self,user='',pwd='',timeout=10):
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        try:
            s.connect((self.host,int(self.port)))
            s.send("stats\r\n")
            result = s.recv(1024)
            if "version" in result:
                self.payload = "username:%s,password:%s" % (user,pwd)
                return True
        except Exception as e:
            log.info(e)
            return False
        finally:
            s.close()

@PluginsManage.register()
class MongodbNoAuth(BaseHostPlugin):
    service  = 'mongodb'
    vul_name = "Mongodb 未授权访问"
    vul_rank = '高'
    vul_desc = """"""
    vul_plan = """"""
    def filter(self,port=27017):
        if self.port == port:
            return True

    def check(self,user='',pwd='',timeout=10):
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        try:
            s.connect((self.host,int(self.port)))
            data = binascii.a2b_hex("3a000000a741000000000000d4070000"
                                    "0000000061646d696e2e24636d640000"
                                    "000000ffffffff130000001069736d61"
                                    "73746572000100000000")
            s.send(data)
            result = s.recv(1024)
            if "ismaster" in result:
                data = binascii.a2b_hex("480000000200000000000000d40700"
                                        "000000000061646d696e2e24636d64"
                                        "000000000001000000210000000267"
                                        "65744c6f6700100000007374617274"
                                        "75705761726e696e67730000")
                s.send(data)
                result = s.recv(1024)
                if "totalLinesWritten" in result:
                    self.payload = "username:%s,password:%s" % (user,pwd)
                    return True
        except Exception as e:
            log.info(e)
            return False
        finally:
            s.close()

@PluginsManage.register()
class PostgresqlNoAuth(BaseHostPlugin):
    service  = 'postgresql'
    vul_name = "Postgresql 未授权访问"
    vul_rank = '高'
    vul_desc = """"""
    vul_plan = """"""

    def filter(self,port=5432):
        if self.port == port:
            return True

    def check(self,user='',pwd='',timeout=10):
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        try:
            sock.connect((self.host,int(self.port)))
            packet_length = len(user) + 7 +len("\x03user  database postgres application_name psql client_encoding UTF8  ")
            p = "%c%c%c%c%c\x03%c%cuser%c%s%cdatabase%cpostgres%capplication_name%cpsql%cclient_encoding%cUTF8%c%c"%( 0,0,0,packet_length,0,0,0,0,user,0,0,0,0,0,0,0,0)
            sock.send(p)
            packet = sock.recv(1024)
            psql_salt=[]
            if packet[0]=='R':
                a = str([packet[4]])
                b = int(a[4:6],16)
                authentication_type = str([packet[8]])
                c = int(authentication_type[4:6],16)
                if c == 5:
                    psql_salt = packet[9:]
                buf=[]
                salt = psql_salt
                lmd5 = self.make_response(buf,user,pwd,salt)
                packet_length1=len(lmd5)+5+len('p')
                pp = 'p%c%c%c%c%s%c'%(0,0,0,packet_length1 - 1,lmd5,0)
                sock.send(pp)
                packet1 = sock.recv(1024)
                if packet1[0] == "R":
                    self.payload = "username:%s,password:%s" % (user,pwd)
                    return True
        except Exception as e:
            log.info(e)
            return False
        finally:
            sock.close()

    def make_response(self,buf,username,password,salt):
        pu = hashlib.md5(password+username).hexdigest()
        buf = hashlib.md5(pu+salt).hexdigest()
        return 'md5' + buf

@PluginsManage.register()
class MssqlNoAuth(BaseHostPlugin):
    service  = 'mssql'
    vul_name = "mssql 未授权访问"
    vul_rank = '高'
    vul_desc = """"""
    vul_plan = """"""
    def filter(self,port=1433):
        if self.port == port:
            return True

    def check(self,user='',pwd='',timeout=10):
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        try:
            sock.connect((self.ip,self.port))
            hh = binascii.b2a_hex(self.ip)
            husername = binascii.b2a_hex(user)
            lusername = len(user)
            lpassword = len(pwd)
            ladd = len(self.host)+len(str(self.port))+1
            hladd = hex(ladd).replace('0x','')
            hpwd = binascii.b2a_hex(pwd)
            pp = binascii.b2a_hex(str(self.port))
            address = hh+'3a'+pp
            hhost = binascii.b2a_hex(self.host)
            data = ("02000200000000001234567890000000"
                    "00000000000000000000000000000000"
                    "000000000000ZZ544000000000000000"
                    "00000000000000000000000000000000"
                    "00000000000X33600000000000000000"
                    "00000000000000000000000000000000"
                    "000000000Y3739333400000000000000"
                    "00000000000000000000000000000000"
                    "000000040301060a0901000000000200"
                    "0000000070796d7373716c0000000000"
                    "00000000000000000000000000000000"
                    "00000712345678900000000000000000"
                    "00000000000000000000000000000000"
                    "00ZZ3360000000000000000000000000"
                    "00000000000000000000000000000000"
                    "00000000000000000000000000000000"
                    "00000000000000000000000000000000"
                    "00000000000000000000000000000000"
                    "00000000000000000000000000000000"
                    "00000000000000000000000000000000"
                    "00000000000000000000000000000000"
                    "00000000000000000000000000000000"
                    "00000000000000000000000000000000"
                    "00000000000000000000000000000000"
                    "00000000000000000000000000000000"
                    "00000000000000000000000000000000"
                    "00000000000000000000000000000000"
                    "00000000000000000000000000000000"
                    "00000000000000000000000000000000"
                    "000Y0402000044422d4c696272617279"
                    "0a00000000000d1175735f656e676c69"
                    "73680000000000000000000000000000"
                    "0201004c000000000000000000000a00"
                    "0000000000000000000000000069736f"
                    "5f310000000000000000000000000000"
                    "00000000000000000000000501353132"
                    "000000030000000000000000")
            data1 = data.replace(data[16:16+len(address)],address)
            data2 = data1.replace(data1[78:78+len(husername)],husername)
            data3 = data2.replace(data2[140:140+len(hpwd)],hpwd)
            if lusername >= 16:
                data4 = data3.replace('0X',str(hex(lusername)).replace('0x',''))
            else:
                data4 = data3.replace('X',str(hex(lusername)).replace('0x',''))
            if lpassword >= 16:
                data5 = data4.replace('0Y',str(hex(lpassword)).replace('0x',''))
            else:
                data5 = data4.replace('Y',str(hex(lpassword)).replace('0x',''))
            hladd = hex(ladd).replace('0x', '')
            data6 = data5.replace('ZZ',str(hladd))
            data7 = binascii.a2b_hex(data6)
            sock.send(data7)
            packet = sock.recv(1024)
            if 'master' in packet:
                self.payload = "username:%s,password:%s" % (user,pwd)
                return True
        except Exception as e:
            log.info(e)
            return False
        finally:
            sock.close()

@PluginsManage.register()
class MysqlNoAuth(BaseHostPlugin):
    service  = 'mysql'
    vul_name = "mysql 未授权访问"
    vul_rank = '高'
    vul_desc = """"""
    vul_plan = """"""

    def filter(self,port=3306):
        if self.port == port:
            return True

    def check(self,user='',pwd='',timeout=10):
        sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
        try:
            sock.connect((self.ip,int(self.port)))
            packet = sock.recv(254)
            plugin,scramble = self.get_scramble(packet)
            if not scramble:
                return False
            auth_data = self.get_auth_data(user,pwd,scramble,plugin)
            sock.send(auth_data)
            result = sock.recv(1024)
            if result == "\x07\x00\x00\x02\x00\x00\x00\x02\x00\x00\x00":
                self.payload = "username:%s ,password:%s"%(user,pwd)
                return True
        except Exception as e:
            log.info(e)
            return False
        finally:
            sock.close()

    def get_hash(self,password, scramble):
        hash_stage1 = hashlib.sha1(password).digest()
        hash_stage2 = hashlib.sha1(hash_stage1).digest()
        to = hashlib.sha1(scramble+hash_stage2).digest()
        reply = [ord(h1) ^ ord(h3) for (h1, h3) in zip(hash_stage1, to)]
        hash = struct.pack('20B', *reply)
        return hash

    def get_scramble(self,packet):
        scramble,plugin = '',''
        try:
            tmp = packet[15:]
            m = re.findall("\x00?([\x01-\x7F]{7,})\x00", tmp)
            if len(m)>3:del m[0]
            scramble = m[0] + m[1]
        except:
            return '',''
        try:
            plugin = m[2]
        except:
            pass
        return plugin,scramble

    def get_auth_data(self,user,password,scramble,plugin):
        user_hex = binascii.b2a_hex(user)
        pass_hex = binascii.b2a_hex(self.get_hash(password,scramble))
        data = "85a23f0000000040080000000000000000000000000000000000000000000000" \
             + user_hex + "0014" + pass_hex
        if plugin:
            data += binascii.b2a_hex(plugin) + "0055035f6f73076f737831302e380c5f" \
                                             + "636c69656e745f6e616d65086c69626d" \
                                             + "7973716c045f7069640539323330360f" \
                                             + "5f636c69656e745f76657273696f6e06" \
                                             + "352e362e3231095f706c6174666f726d" \
                                             + "067838365f3634"
        len_hex = hex(len(data)/2).replace("0x","")
        auth_data = len_hex + "000001" +data
        return binascii.a2b_hex(auth_data)

@PluginsManage.register()
class FtpNoAuth(BaseHostPlugin):
    service  = 'ftp'
    vul_name = "ftp 未授权访问"
    vul_rank = '高'
    vul_desc = """"""
    vul_plan = """"""
    def filter(self,port=21):
        if self.port == port:
            return True

    def check(self,user='',pwd='',timeout=10):
        try:
            ftp = ftplib.FTP()
            ftp.connect(self.host,self.port)
            ftp.login(user,pwd)
            self.payload = "username:%s ,password:%s"%(user,pwd)
            return True
        except Exception as e:
            log.info(e)
            return False

@PluginsManage.register()
class TelnetNoAuth(BaseHostPlugin):
    service  = 'telnet'
    vul_name = "telnet 未授权访问"
    vul_rank = '高'
    vul_desc = """"""
    vul_plan = """"""
    def filter(self,port=23):
        if self.port == port:
            return True

    def check(self,user='',pwd='',timeout=10):
        try:
            tn = telnetlib.Telnet(self.host,self.port,30)
            #tn.set_debuglevel(3)
            time.sleep(0.5)
            op = tn.read_some()
        except Exception as e:
            log.info(e)
            return False
        user_match = "(?i)(login|user|username)"
        pass_match = '(?i)(password|pass)'
        login_match = '#|\$|>'
        if re.search(user_match,op):
            try:
                tn.write(str(user)+'\r\n')
                tn.read_until(pass_match,timeout=2)
                tn.write(str(pwd)+'\r\n')
                login_info=tn.read_until(login_match,timeout=3)
                tn.close()
                if re.search(login_match,login_info):
                    self.payload = "username:%s,password:%s" % (user,pwd)
                    return True
            except Exception as e:
                pass
        else:
            try:
                info = tn.read_until(user_match,timeout=2)
            except Exception as e:
                return False
            if re.search(user_match,info):
                try:
                    tn.write(str(pwd)+'\r\n')
                    tn.read_until(pass_match,timeout=2)
                    tn.write(str(pwd)+'\r\n')
                    login_info=tn.read_until(login_match,timeout=3)
                    tn.close()
                    if re.search(login_match,login_info):
                        self.payload = "username:%s,password:%s" % (user,pwd)
                        return True
                except Exception as e:
                    return False
            elif re.search(pass_match,info):
                tn.read_until(pass_match,timeout=2)
                tn.write(str(pass_)+'\r\n')
                login_info=tn.read_until(login_match,timeout=3)
                tn.close()
                if re.search(login_match,login_info):
                    self.payload = "password:%s" % (pwd)
                    return True

'''
@PluginsManage.register()
class RdpNoAuth(BaseHostPlugin):
    vul_name = "Rdp 未授权访问"
    vul_rank = '高'
    vul_desc = """"""
    vul_plan = """"""
    def filter(self,port=3389):
        if self.port == port:
            return True

    def check(self,user='',pwd=''):
        pass

@PluginsManage.register()
class SshNoAuth(BaseHostPlugin):
    vul_name = "Ssh 未授权访问"
    vul_rank = '高'
    vul_desc = """"""
    vul_plan = """"""
    def filter(self,port=22):
        if self.port == port:
            return True

    def check(self,user='',pwd=''):
        pass  
'''

